home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 12 - 1996 / 12.10 Oct 96 / ChallyDebugCTB / Code / DebugTerm.c next >
Encoding:
Text File  |  1996-05-26  |  16.3 KB  |  636 lines  |  [TEXT/CWIE]

  1. /* DebugTerm.c
  2.  
  3. This file can be included in a C project or compiled as a 
  4. library.  It allows the programmer to send debugging 
  5. information through a Communications Toolbox connection 
  6. to any terminal program.  If you wish to use the ADSP tool, 
  7. you will want to use it with one that supports the 
  8. Communications Toolbox.  
  9.  
  10. In theory, DebugTerm works with any connection tool.  In 
  11. practice, it will probably be used with the ADSP tool to 
  12. send debugging information to a terminal program on the same
  13. Macintosh (it works!), or to one running on another 
  14. Macintosh somewhere on the network. However, it may 
  15. alternatively be used with the serial tool to send debugging 
  16. information to a terminal program on another computer or to
  17. a dumb terminal, or perhaps even a serial device such as a
  18. printer or modem.
  19.  
  20. Assumptions: 
  21.     The connection tool has no "persistent" windows or menus
  22.         that need the support of the host application!
  23.     You will not attempt to send data from the connected
  24.         terminal or device TO Debugterm.
  25.     You are running a machine and OS that has/supports
  26.         Communications Toolbox.
  27.     You will not attempt to cram more into the CTB buffers
  28.         than they can hold.
  29.     
  30. Problems:
  31.     Can't find a way to tell if the host application has
  32.         already initialized the Communications Toolbox.
  33.     Can't find a way to tell if the user (you) attempts to
  34.         connect synchronously to a process on the same
  35.         machine (won't work).
  36.     It doesn't notice if the connection goes away (such as
  37.         is possible with the ADSP driver).
  38.  
  39. Following is a summary of external functions used with DebugTerm:
  40.  
  41. OpenDebugTerm: Used to launch DebugTerm, initiate a 
  42.     connection, and prepare to send debugging information to 
  43.     the recipient.
  44.         
  45. CloseDebugTerm: Used to close connection and release 
  46.     resources in use by DebugTerm.
  47.         
  48. DebugTerm: Used to send a Str255 containing debugging 
  49.     information to DebugTerm, which it will in turn send 
  50.     through the connection to the recipient. (This was the
  51.     original test call.)
  52.     
  53. debugf: Meant to mimic c the stdio library printf,
  54.     (see K & R, especially 2nd ed. pp. 155 and 156) it uses
  55.     a first parameter that is a format string and an
  56.     arbitrary number of additional parameters which match
  57.     the specifications in the format string.
  58.     
  59.          1         2         3         4         5         6         7         8
  60. 12345678901234567890123456789012345678901234567890123456789012345678901234567890
  61. */
  62.  
  63. // Necessary includes
  64. //#include "Connections.h"
  65. //#include "CommResources.h"
  66. #include "stdarg.h"
  67. #include "DebugTerm.h"
  68.  
  69. // Private literals
  70. #define kPrefsType            'rsrc'    // Call it a ResEdit 
  71. #define kAppSignature        'RSED'    // resource file
  72. #define kClassCM            'cbnd'
  73. #define rConnClassID        1001
  74. #define rFirstToolConfigID    2001
  75.  
  76. // Global variables
  77. static ConnHandle         gConn = nil;
  78. static Boolean            gAsyncCalls, gCTBInited = false;
  79. static CMFlags            gFlags;
  80.  
  81. //**********************************************************
  82. //
  83. // pStrCopy - Copy the value of p1 to p2
  84. //
  85. //**********************************************************
  86. static void pStrCopy(unsigned char *p1, unsigned char *p2)
  87. {
  88.     register short len = *p2++ = *p1++;
  89.     while (--len >= 0)
  90.         *p2++=*p1++;
  91. }
  92.  
  93. //**********************************************************
  94. //
  95. // GetOpenFileDir - Gets VRef and ParentDirId for open file
  96. //
  97. //**********************************************************
  98. static OSErr GetOpenFileDir(short openFileRef, short *vRef, 
  99.     long *parDirID)
  100. {
  101.     FCBPBRec pb;
  102.     OSErr theErr;
  103.         
  104.     pb.ioCompletion = nil;
  105.     pb.ioFCBIndx = 0;
  106.     pb.ioVRefNum = 0;
  107.     pb.ioRefNum = openFileRef;
  108.     pb.ioNamePtr = nil;
  109.     
  110.     theErr = PBGetFCBInfo(&pb, false);
  111.     
  112.     *vRef = pb.ioFCBVRefNum;
  113.     *parDirID = pb.ioFCBParID;
  114.     
  115.     return theErr;
  116. }
  117.  
  118. //**********************************************************
  119. //
  120. // OpenPrefsFile - Opens prefs, creates if not found
  121. //
  122. //**********************************************************
  123. static OSErr OpenPrefsFile(short *myRefNum, Boolean createIt)
  124. {
  125.     Str255    myFileName;
  126.     long    myDirID;
  127.     short    myVRef, appResRef;
  128.     OSErr    theErr;
  129.     FSSpec    mySpec, appDirSpec;
  130.     Boolean    found;
  131.         
  132.     *myRefNum = -1;
  133.     found = false;
  134.     pStrCopy("\pDebugTerm Prefs", myFileName);
  135.     appResRef = CurResFile();
  136.  
  137.     theErr = GetOpenFileDir(appResRef, &myVRef, &myDirID);
  138.     if (!theErr) {
  139.         theErr = FSMakeFSSpec(myVRef, myDirID, myFileName, &appDirSpec);
  140.         if (!theErr) {
  141.             found = true;
  142.             BlockMove(&appDirSpec, &mySpec, sizeof(FSSpec));
  143.         }
  144.     }
  145.         
  146.     if ( (theErr == fnfErr) && createIt ) {
  147.         BlockMove(&appDirSpec, &mySpec, sizeof(FSSpec));
  148.         FSpCreateResFile(&mySpec, kAppSignature, kPrefsType, 0);
  149.         theErr = ResError();
  150.     }
  151.     
  152.     if (!theErr) {
  153.         *myRefNum = FSpOpenResFile(&mySpec, fsRdWrPerm);
  154.         theErr = ResError();
  155.     }
  156.     
  157.     return theErr;
  158. }
  159.  
  160. //**********************************************************
  161. //
  162. // CMCompetion - Completion routine for the Connection Mgr
  163. //
  164. //**********************************************************
  165. static pascal void CMCompletion(ConnHandle hConn)
  166. {
  167.     // Should at least check to see if (**hConn).errCode 
  168.     // is non-zero.  Perhaps use hConn to put any error 
  169.     // code in UserData for the main program to flush out.
  170. }
  171.  
  172. //**********************************************************
  173. //
  174. // CMSendProc - Sends the data out through the connection
  175. //
  176. //**********************************************************
  177. static pascal OSErr CMSendProc(Ptr thePtr, long theSize, CMFlags flags)
  178. {
  179.     long            count, returnVal;
  180.     EventRecord        theEvent;
  181.     CMBufferSizes    sizes;
  182.     OSErr            theErr;
  183.     CMStatFlags        statFlags;
  184.     
  185.     returnVal = 0;
  186.     
  187.     if (gConn) {
  188.         count = theSize;        // Give other processes time while
  189.         if (gAsyncCalls) do {    // waiting for previous write to finish
  190.             if (theErr = CMStatus(gConn, sizes, &statFlags))
  191.                 return theErr;
  192.             if (statFlags & (cmStatusDWPend + cmStatusOpening))
  193.                 WaitNextEvent(nullEvent, &theEvent, 1, nil);
  194.         } while (statFlags & (cmStatusDWPend + cmStatusOpening));
  195.         if (theErr = CMWrite(gConn, thePtr, &count, cmData,
  196.                 gAsyncCalls, CMCompletion, -1, statFlags)) 
  197.             return theErr;
  198.     }
  199.     return noErr;
  200. }
  201.  
  202. //**********************************************************
  203. //
  204. // InitCTBStuff - Initializes comm toolbox
  205. //
  206. //**********************************************************
  207. static OSErr InitCTB(void)
  208. {
  209.     short             theErr;
  210.  
  211.     if (!gCTBInited) {
  212.         if (theErr = InitCTBUtilities())
  213.             return theErr;
  214.         if (theErr = InitCRM())
  215.             return theErr;
  216.         if (theErr = InitCM())
  217.             return theErr;
  218.         gCTBInited = true;
  219.     }
  220.     return noErr;
  221. }
  222.  
  223. //**********************************************************
  224. //
  225. // FindTool - Tries to get the choice tool if 
  226. //                available, else tries first available.
  227. //
  228. //**********************************************************
  229. static OSErr FindTool(short *procID, Str255 toolName)
  230. {
  231.     Handle            rHand;
  232.     StringHandle    sH;
  233.     OSErr            theErr;
  234.  
  235.     *procID = -1;
  236.     theErr = noErr;
  237.     
  238.     // Get name of tool last used, and its procID
  239.     rHand = GetResource(kAppSignature, rConnClassID);
  240.     if (rHand) {
  241.         sH = (StringHandle)rHand;
  242.         pStrCopy(*sH, toolName);
  243.         *procID = CMGetProcID(toolName);
  244.     }
  245.     if (*procID == -1 ) {
  246.         // may not actually have a resource file open, or the
  247.         // tool we want is no longer in the extensions folder so
  248.         // get the name and procID of the first tool we find
  249.         if (!(theErr = CRMGetIndToolName(kClassCM, 1, toolName)))
  250.             *procID = CMGetProcID(toolName);
  251.         if (*procID == -1)
  252.             theErr = ResError();
  253.         if (!theErr)
  254.             theErr = kToolNotAvailErr;
  255.     }
  256.     return theErr;
  257. }
  258.  
  259. //**********************************************************
  260. //
  261. // ReadPrefs - Read prefs for tool of specified class
  262. //
  263. //**********************************************************
  264. static OSErr ReadPrefs(short *procID, Handle *configH)
  265. {
  266.     short     resFileRef;
  267.     Str255    toolName;
  268.     Handle    resH;
  269.     OSErr    theErr;
  270.     
  271.     theErr = OpenPrefsFile(&resFileRef, kDontCreate);
  272.     // if we can't open prefs, we can still get a tool
  273.     theErr = FindTool(procID, toolName);
  274.     *configH = nil;
  275.     
  276.     if (resFileRef != -1) {
  277.         if (!theErr) {
  278.             // if the resource file was opened, get the 
  279.             // configuration string from a resource
  280.             resH = GetNamedResource(kAppSignature, toolName);
  281.             if (resH) {
  282.                 *configH = resH;
  283.                 HandToHand(configH);
  284.                 ReleaseResource(resH);
  285.             }  else
  286.                 theErr = ResError();
  287.         }
  288.         CloseResFile(resFileRef);
  289.     }
  290.     return theErr; // could be returning kToolNotAvailErr
  291. }
  292.  
  293. //**********************************************************
  294. //
  295. // WritePrefs - Create/Update connection tool prefs
  296. //
  297. //**********************************************************
  298. static OSErr WritePrefs(ConnHandle hConn)
  299. {
  300.     Ptr                specP;
  301.     Handle            specH, resH;
  302.     Str255            tName, tClassName;
  303.     OSErr            theErr;
  304.     short            resID, resFileRef;
  305.     Size            hSize;
  306.     StringHandle    sH;
  307.     
  308.     if (!hConn)
  309.         return kNoConnectionErr;
  310.     
  311.     // Get the current tool's name and its config string
  312.     CMGetToolName((**hConn).procID, tName);
  313.     specP = CMGetConfig(hConn);
  314.     if (!specP)
  315.         return kGetConfigErr;
  316.     
  317.     if (theErr = OpenPrefsFile(&resFileRef, kDoCreate))
  318.         return theErr;
  319.             
  320.     // Update/Create resource specifying tool name
  321.     resID = rConnClassID;
  322.     pStrCopy("\pConnection Tool Name", tClassName);
  323.     sH = NewString(tName);
  324.     if (sH) {
  325.         hSize = GetHandleSize((Handle)sH);
  326.         resH = Get1Resource(kAppSignature, resID);
  327.         theErr = ResError();
  328.         if ((!resH) && ((theErr == resNotFound) || (theErr == noErr))) {
  329.             AddResource((Handle)sH, kAppSignature, resID, tClassName);
  330.             if (!(theErr = ResError()))
  331.                 WriteResource((Handle)sH);
  332.             if (!theErr)
  333.                 theErr = ResError();
  334.             ReleaseResource((Handle)sH);
  335.         } else if (resH) {
  336.             SetHandleSize(resH, hSize);
  337.             if (!(theErr = MemError())) {
  338.                 BlockMove(*sH, *resH, hSize);
  339.                 ChangedResource(resH);
  340.                 if (!(theErr = ResError()))
  341.                     WriteResource(resH);
  342.                 if (!theErr)
  343.                     theErr = ResError();
  344.             }
  345.             ReleaseResource(resH);
  346.         }
  347.         DisposeHandle((Handle)sH);
  348.     } else
  349.         theErr = MemError();
  350.     
  351.     // Update/Create resource with the tool config string
  352.     if (!theErr) {
  353.         hSize = GetPtrSize(specP);
  354.         resH = Get1NamedResource(kAppSignature, tName);
  355.         theErr = ResError();
  356.         if ( (!resH) && ((theErr == resNotFound) || (theErr == noErr)) ) {
  357.             theErr = noErr;
  358.             specH = NewHandle(hSize);
  359.             if (specH) {
  360.                 BlockMove(specP, *specH, hSize);
  361.                 do
  362.                     resID = UniqueID(kAppSignature);
  363.                 while (resID <= rFirstToolConfigID);
  364.                 AddResource(specH, kAppSignature, resID, tName);
  365.                 if (!(theErr = ResError()))
  366.                     WriteResource(specH);
  367.                 if (!theErr)
  368.                     theErr = ResError();
  369.                 ReleaseResource(specH);
  370.                 DisposeHandle(specH);
  371.             } else
  372.                 theErr = MemError();
  373.         } else if (resH) {
  374.             SetHandleSize(resH, hSize);
  375.             if (!(theErr = MemError())) {
  376.                 BlockMove(specP, *resH, hSize);
  377.                 ChangedResource(resH);
  378.                 if (!(theErr = ResError()))
  379.                     WriteResource(resH);
  380.                 if (!theErr)
  381.                     theErr = ResError();
  382.             }
  383.             ReleaseResource(resH);
  384.         }
  385.     }
  386.     
  387.     DisposePtr(specP);
  388.     CloseResFile(resFileRef);
  389.     
  390.     return theErr;
  391. }
  392.  
  393. //**********************************************************
  394. //
  395. // ChooseConnection - Poses Connection Settings dialog
  396. //        allowing the user to choose and configure a tool
  397. //
  398. //**********************************************************
  399. static OSErr ChooseConnection(ConnHandle *hConn)
  400. {
  401.     Point    where;
  402.     short    result;
  403.     Boolean    returnVal;
  404.     
  405.     returnVal = kNothingChosenErr;
  406.     
  407.     if (*hConn) {
  408.         SetPt(&where, 20, 40);
  409.         HUnlock((Handle)(*hConn));
  410.         result = CMChoose(hConn, where, nil);
  411.         HLock((Handle)(*hConn));
  412.         switch (result) {
  413.             case chooseDisaster:
  414.             case chooseFailed:
  415.                 returnVal = kNothingChosenErr;
  416.                 break;
  417.             case chooseOKMinor:
  418.             case chooseOKMajor:
  419.                 returnVal = noErr;
  420.                 break;
  421.             default:
  422.                 returnVal = result;
  423.                 break;
  424.         }
  425.     }
  426.     return returnVal;
  427. }
  428.  
  429. //**********************************************************
  430. //
  431. // OpenDebugTerm - Open connection to the debugging terminal
  432. //
  433. //**********************************************************
  434. OSErr OpenDebugTerm(Boolean async, Boolean initCTB, Boolean prompt)
  435. {
  436.     short            procID;
  437.     Handle            configH;
  438.     CMBufferSizes    sizes;
  439.     OSErr             theErr;
  440.     Boolean            newTool;
  441.     
  442.     if (gConn)
  443.         return kAlreadyOpenErr;
  444.     
  445.     if (initCTB)
  446.         if (theErr = InitCTB())
  447.             return theErr;
  448.     
  449.     // Find user's preferred tool and config string
  450.     // or get defaults if blown or do not exist
  451.     theErr = ReadPrefs(&procID, &configH);
  452.     if (theErr == kToolNotAvailErr) {
  453.         newTool = true;
  454.         theErr = noErr;    // if problem is tool changed,
  455.     } else if (theErr)    // make note, else pitch
  456.         return theErr;
  457.     else
  458.         newTool = false;
  459.         
  460.     if (procID == -1)
  461.         return kNoConnToolErr;
  462.         
  463.     sizes[cmDataIn] = 0; // Asking for zero means leave 
  464.     sizes[cmDataOut] = 0; // buffer size up to tool
  465.     sizes[cmCntlIn] = 0;
  466.     sizes[cmCntlOut] = 0;
  467.     sizes[cmAttnIn] = 0;
  468.     sizes[cmAttnOut] = 0;
  469.     
  470.     // Get a new Connection Tool record
  471.     // and set config string as saved
  472.     gConn = CMNew(procID, cmData, sizes, 0, 0);
  473.     if (!gConn) {
  474.         if (configH)
  475.             DisposeHandle(configH);
  476.         return kCMNewErr;
  477.     } else if (configH) {
  478.         HLock(configH);     // don't let memory manager move  
  479.         CMSetConfig(gConn, *configH);    // dereferenced pointer
  480.         HUnlock(configH);
  481.         DisposeHandle(configH);
  482.     }
  483.     MoveHHi((Handle)gConn);
  484.     HLock((Handle)gConn);     // lock to access data at interrupt level
  485.     
  486.     // pose Connection Settings dialog if desired or desired tool was
  487.     // not available or configuration string was not available
  488.     theErr = noErr;
  489.     if (prompt || newTool || !configH)
  490.         if (ChooseConnection(&gConn))
  491.             theErr = kNothingChosenErr;
  492.  
  493.     // write resulting new preferences
  494.     if (!theErr) {
  495.         WritePrefs(gConn); // may want to complain
  496.         theErr = CMOpen(gConn, async, CMCompletion, -1);
  497.     }
  498.     
  499.     if (theErr) {
  500.         if (gConn)    // if ChooseDisaster, gConn already disposed & nil
  501.             CMDispose(gConn);
  502.         gConn = nil;
  503.         return theErr;
  504.     }
  505.  
  506.     gFlags = false;
  507.     gAsyncCalls = async;
  508.         
  509.     return noErr;
  510. }
  511.  
  512. //**********************************************************
  513. //
  514. // CloseDebugTerm - Close connection to debugging terminal
  515. //
  516. //**********************************************************
  517. OSErr CloseDebugTerm(void)
  518. {
  519.     OSErr             theErr;
  520.     CMBufferSizes    sizes;
  521.     CMStatFlags        statFlags;
  522.     
  523.     if (!gConn)                    // close only if opened
  524.         return kNoConnectionErr;        // or opening
  525.  
  526.     // close the connection, but only if it's open or trying to open
  527.     theErr = CMStatus(gConn, sizes, &statFlags);
  528.     if (statFlags & (cmStatusOpen + cmStatusOpening))
  529.         theErr = CMClose(gConn, gAsyncCalls, CMCompletion, -1, false);
  530.     HUnlock((Handle)gConn);
  531.     CMDispose(gConn);
  532.     gConn = nil;
  533.     
  534.     return theErr;
  535. }
  536.  
  537. //**********************************************************
  538. //
  539. // DebugTerm - Sends pascal string out through connection
  540. //
  541. //**********************************************************
  542. OSErr DebugTerm(Str255 theStr)
  543. {
  544.     long    theSize;
  545.     char    *thePtr;
  546.     OSErr    theErr;
  547.     
  548.     if (!gConn)
  549.         return kNoConnectionErr;
  550.         
  551.     // pass length byte as theSize and next byte as address
  552.     theSize = *theStr;
  553.     thePtr = (char *)theStr + 1;
  554.     if (theErr = CMSendProc(thePtr, theSize, gFlags))
  555.         return theErr;
  556.         
  557.     return noErr;
  558. }
  559.  
  560. //**********************************************************
  561. //
  562. // debugf - Looks suspiciously like minprintf(),
  563. //                found in K&R 2nd ed. page 156!
  564. //
  565. //**********************************************************
  566. OSErr debugf(char *fmt, ...)
  567. {
  568.     va_list ap;    // points to each unnamed argument
  569.     char     *p, *q, *cStr, *pStr;
  570.     short    len;
  571.     Str255    s;
  572.     long    lVal;
  573.     OSErr    theErr;
  574.     //Rect    r;
  575.     
  576.     if (!gConn)
  577.         return kNoConnectionErr;
  578.         
  579.     va_start(ap, fmt); // point ap to first unnamed argument
  580.     for (p = fmt; *p; p++) {
  581.         if (*p != '%') { // sluggish, but works
  582.             if (theErr = CMSendProc(p, 1, gFlags))
  583.                 return theErr;
  584.             continue;
  585.         }
  586.         switch(*++p) {
  587.             case 'P':
  588.             case 'p': // output a p string
  589.                 pStr = va_arg(ap, char *);
  590.                 len = *pStr;
  591.                 theErr = CMSendProc(++pStr, len, gFlags);
  592.                 break;
  593.             case 'C':
  594.             case 'c': // output a c string
  595.                 cStr = va_arg(ap, char *);
  596.                 for (len = 0, q = cStr; *q++ != '\0'; len++);
  597.                 theErr = CMSendProc(cStr, len, gFlags);
  598.                 break;
  599.             case 'I':    // treat int and short differently
  600.             case 'i':    // for compatibility 
  601.                 lVal = va_arg(ap, int);
  602.                 NumToString(lVal, s);
  603.                 q = (char*)s;
  604.                 theErr = CMSendProc(++q, *s, gFlags);
  605.                 break;
  606.             case 'S':
  607.             case 's': 
  608.                 lVal = va_arg(ap, short);
  609.                 NumToString(lVal, s);
  610.                 q = (char*)s;
  611.                 theErr = CMSendProc(++q, *s, gFlags);
  612.                 break;
  613.             case 'L':
  614.             case 'l': // output long integer
  615.                 NumToString(va_arg(ap, long), s);
  616.                 q = (char*)s;
  617.                 theErr = CMSendProc(++q, *s, gFlags);
  618.                 break;
  619.             /*case 'R': // possible %r specifier
  620.             case 'r':
  621.                 r = va_arg(ap, Rect);
  622.                 theErr = debugf("{%s, %s, %s, %s}", 
  623.                     r.top, r.left, r.bottom, r.right);
  624.                 break;*/
  625.             default:
  626.                 theErr = CMSendProc(p, 1, gFlags);
  627.                 break;
  628.         }
  629.         if (theErr) {
  630.             va_end(ap);
  631.             return theErr;
  632.         }
  633.     }
  634.     va_end(ap);
  635.     return noErr;
  636. }